package cc.aliza.lib.mongo.core.encoder;

import cc.aliza.lib.mongo.core.annotations.EmbedList;
import cc.aliza.lib.mongo.core.utils.MapperUtil;
import org.bson.Document;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;

/**
 * 『给时光以生命·给岁月以文明』
 *
 * @author Zhang Jing (byrdkm17@gmail.com).
 */
@SuppressWarnings("unchecked")
public class EmbedListEncoder extends AbstractEncoder {

    public EmbedListEncoder(Object obj, Field field) {
        super(obj, field);
    }

    @Override
    public String getFieldName() {
        EmbedList embedList = field.getAnnotation(EmbedList.class);
        return getFieldRealName(embedList.name());
    }

    @Override
    public Object encode() {
        Object result = null;
        Class<?> type = field.getType();
        if (type.isArray()) {
            result = encodeArray();
        } else {
            ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType();
            Type[] types = parameterizedType.getActualTypeArguments();
            if (types.length == 1) {
                result = encodeCollection();
            }
            if (types.length == 2) {
                result = encodeMap();
            }
        }

        return result;
    }

    private Object encodeArray() {
        int len = Array.getLength(value);
        List<Document> result = new ArrayList<>();
        for (int i = 0; i < len; i++) {
            Object o = Array.get(value, i);
            if (o != null) {
                result.add(MapperUtil.toDocument(o, true));
            }
        }
        return result;
    }

    private Object encodeCollection() {
        List<Document> result = new ArrayList<>();
        Collection collection = (Collection) value;
        collection.stream().filter(o -> o != null).forEach(o -> result.add(MapperUtil.toDocument(o, true)));
        return result;
    }

    private Object encodeMap() {
        Map result = new HashMap<>();
        Map map = (Map) value;
        map.entrySet().stream()
                .filter(o -> ((Map.Entry) o).getValue() != null)
                .forEach(o -> {
                    Map.Entry entry = (Map.Entry) o;
                    result.put(entry.getKey(), MapperUtil.toDocument(entry.getValue(), true));
                });
        return result;
    }
}
